package com.pkx.guide.info.service.impl;

import com.pkx.guide.common.util.StringUtils;
import com.pkx.guide.info.service.SansiService;
import com.pkx.guide.info.service.SocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

/**
 * 三思发布 服务实现类
 *
 * @author sunsy
 * @since 2022-03-10
 */
@Service
public class SansiServiceImpl implements SansiService {

    private int[] crcTable = {
            0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
            0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
            0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
            0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
            0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
            0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
            0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
            0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
            0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
            0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
            0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
            0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
            0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
            0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
            0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
            0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
            0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
            0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
            0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
            0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
            0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
            0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
            0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
            0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
            0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
            0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
            0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
            0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
            0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
            0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
            0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
            0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
    };

    @Autowired
    private SocketService socketService;

    @Override
    public String genPlaylst(String pictureRoute) {
        if (StringUtils.isEmpty(pictureRoute)){
            throw new IllegalArgumentException("图片路径为空！");
        }
        Integer item_no = 0;
        String filename = pictureRoute.substring(pictureRoute.lastIndexOf('\\')+1);
        filename = filename.substring(filename.lastIndexOf('/') + 1, filename.lastIndexOf('.'));
        System.out.println("图片名称：" + filename);
        StringBuffer buf = new StringBuffer();
        buf.append("[list]");
        buf.append(System.getProperty("line.separator"));
        buf.append("item_no=1");
        buf.append(System.getProperty("line.separator"));
        buf.append(String.format("item%d=500,1,0,\\B%s",0,filename));
        buf.append(System.getProperty("line.separator"));
        return buf.toString();
    }

    private byte[] ASCIIToBytes(String str) {
        Integer len = str.length();
        byte[] bt = new byte[len];

        for (int i = 0; i < len; ++i)
        {
            bt[i] = (byte)str.charAt(i);
        }
        return bt;
    }

    private byte[] intToByte4B(int n) {
        byte[] b = new byte[4];
        b[0] = (byte)(n >> 24 & 0xff);
        b[1] = (byte)(n >> 16 & 0xff);
        b[2] = (byte)(n >> 8 & 0xff);
        b[3] = (byte)(n & 0xff);
        return b;
    }

    private byte[] Packet(Integer slaveID, String pkgType, byte[] data)
    {
        Integer len = data == null ? 0 : data.length;
        Integer pos = 0;
        byte[] ret = new byte[len + 8];
        ret[pos++] = 0x02;
        String sid = String.format("%02d", slaveID);
        ret[pos++] = (byte) (sid.charAt(0));
        ret[pos++] = (byte) (sid.charAt(1));
        ret[pos++] = (byte) (pkgType.charAt(0));
        ret[pos++] = (byte) (pkgType.charAt(1));

        if (len > 0)
        {
            System.arraycopy(data, 0, ret, 5, len);
        }
        pos += len;

        //校验
        byte[] crcBytes = new byte[ret.length-4];
        System.arraycopy(ret, 1, crcBytes, 0, crcBytes.length);
        byte[] crc2 = getCrc(crcBytes, crcBytes.length);
        ret[pos++] = crc2[0];
        ret[pos++] = crc2[1];

        ret[pos] = 0x03;

        //转义
        byte[] sendPkg = TransPkg(ret);

        return sendPkg;
    }

    static byte[] TransPkg(byte[] buf)
    {
        // 帧数据或帧校验中如果有某个字符等于帧头、帧尾或0x1b，则在发送时需转为两个字节
        // 0x02 -> 0x1b, 0xe7
        // 0x03 -> 0x1b, 0xe8
        // 0x1b -> 0x1b, 0x00
        ArrayList<Byte> li = new ArrayList<>();
        for(int i=0;i<buf.length;++i) {
            li.add(buf[i]);
        }
        //前5位是：1帧头 + 2地址 + 2类型，li.Count - 1是去掉帧尾
        byte r = 0x1b; //要转义成什么
        for (int i = 5; i < li.size() - 1; ++i)
        {
            byte b = (byte)(li.get(i));
            switch (b)
            {
                case 0x02: r = (byte) 0xe7; break;
                case 0x03: r = (byte) 0xe8; break;
                case 0x1b: r = (byte) 0x1b; break;
            }
            //如果有转义
            if (r != 0x1b)
            {
                li.add(i, (byte)0x1b); //在前面插入
                ++i;
                li.set(i, r);
                r = (byte) 0x1b;
            }
        }
        byte[] ret = new byte[li.size()];
        for(int i=0;i<li.size();++i) {
            ret[i] = li.get(i);
        }
        return ret;
    }

    private byte [] shortToByteArray(short  s) {
        byte [] shortBuf = new byte [2];
        for(int i=0;i<2;i++) {
            int offset = (shortBuf.length - 1 -i)*8;
            shortBuf[i] = (byte )((s>>>offset)&0xff);
        }
        return shortBuf;
    }

    /**
     * 获取CRC校验
     * @param data
     * @param len
     * @return
     */
    private byte[] getCrc(byte[] data, int len) {
        if (len == 0) len = data.length;
        short crc = 0;
        for (int i = 0; i < len; i++) {
            crc = (short) (crcTable[((crc >> 8) ^ data[i]) & 0xff] ^ (crc << 8));
        }
        return shortToByteArray(crc);
    }

    @Override
    public Boolean ULOneStrFile(String playlst, String fileDest, String ip, Integer port, Integer slaveid) {
        Boolean ret = true;
        byte[] btFile = playlst.getBytes(StandardCharsets.UTF_8);
        Integer sendTimes = btFile.length / 2048 + 1;
        Integer sendLen = 0;
        Integer pos = fileDest.length();
        for(int i = 0; i < sendTimes; ++i) {
            sendLen = (sendTimes - i - 1 > 0) ? 2048 : btFile.length % 2048;
            byte[] data = new byte[fileDest.length() + 1 + 4 + sendLen];
            System.arraycopy(ASCIIToBytes(fileDest),0, data,0, fileDest.length());
            data[pos] = 0x2b;
            //小端返回，转大端
            byte[] btOffset = intToByte4B(2048 * i);
            System.arraycopy(btOffset, 0, data, pos + 1, 4);
            System.arraycopy(btFile, i * 2048, data, pos + 5, sendLen);
            byte[] sendPkg = Packet(slaveid, "10", data);
            try {
                Socket socket = socketService.clientSocket(ip,port);
                socketService.send(sendPkg, socket);
                byte[] recvPkg = socketService.receive(socket);
                if(recvPkg == null) {
                    ret = false;
                    break;
                }
            } catch (Exception e){
                e.printStackTrace();
                ret = false;
            }
            if(i +1 < sendTimes) {
//                Thread.sleep(200);
            }
        }
        return ret;
    }
}
